import {
  Body,
  Controller,
  Delete,
  Get,
  HttpException,
  HttpStatus,
  Param,
  Post,
  Put,
  Query,
  Session,
  UseGuards,
} from '@nestjs/common';
import { ArticleService } from './article.service';
import {
  ArticleCreateRequest,
  ArticleUpdateRequest,
  GetArticlesRequest,
} from './dto/request.dto';
import { writeFile, unlink, readFile } from 'fs/promises';
import { ResponseBody, ResponseCode } from '@/utils/ResponseCode';
import { join } from 'path';
import { articleDir } from '@/config/sourceFile';
import { TagService } from '../tag/tag.service';
import { Article as ArticleEntity } from '@/entity/Article';
import { SessionGuard } from '@/guard/session.guard';
import { SessionType } from '@/types';
import { toArray } from 'lodash';
import { safeWriteFile } from '@/utils/fsPro';

@UseGuards(SessionGuard)
@Controller('article')
export class ArticleController {
  constructor(
    private readonly articleService: ArticleService,
    private readonly tagService: TagService,
  ) {}

  @Get()
  async getArticles(@Query() query: GetArticlesRequest) {
    const { current, pageSize, tagIds = [], author, ...findWhere } = query;

    const { list, total } = await this.articleService.findAllAndCount(
      {
        ...findWhere,
        tagIds: toArray(tagIds),
        user: author ? { nickname: author } : undefined,
      },
      {
        current,
        pageSize,
      },
    );
    return new ResponseBody<{
      total: number;
      list: ArticleEntity[];
    }>({
      data: {
        total,
        list,
      },
      message: '获取文章列表成功',
    });
  }

  @Post()
  async createArticle(
    @Body() requestBody: ArticleCreateRequest,
    @Session() session: SessionType,
  ) {
    const { content, outline, userId, ...toAddArticle } = requestBody;
    const suffixP = '.md'; //后缀
    const filename = toAddArticle.headline + new Date().getTime() + suffixP;

    try {
      const [, articleEntity] = await Promise.all([
        safeWriteFile(join(articleDir, filename), content),
        this.articleService.create({
          ...toAddArticle,
          filename,
          // 如果没有outline，自动给content的内容
          outline: outline ? outline : content.slice(0, 50),
          // 如果不传userId，那么相当于登入者自己创建
          userId: userId || session.userId,
        }),
      ]);
      return new ResponseBody<number>({
        message: '创建文章成功',
        data: articleEntity.id,
      });
    } catch (error) {
      console.log(error);
      throw new HttpException(
        {
          message: error,
          code: ResponseCode.Fail,
          data: null,
        },
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }

  @Put(':id')
  async updateArticle(
    @Param('id') id: number,
    @Body() requestBody: ArticleUpdateRequest,
  ) {
    const toUpdateArticle = requestBody;
    const articleEntity = await this.articleService.getArticleByWhere({ id });
    if (!articleEntity) {
      throw new HttpException(
        {
          message: '文章不存在',
          code: ResponseCode.Fail,
          data: null,
        },
        HttpStatus.NOT_FOUND,
      );
    }
    const filename = articleEntity.filename;
    try {
      await Promise.all([
        // 如果需要更新内容，重新写入文件
        toUpdateArticle.content
          ? safeWriteFile(join(articleDir, filename), toUpdateArticle.content)
          : Promise.resolve(),
        this.articleService.update(
          {
            id,
          },
          toUpdateArticle,
        ),
      ]);
      return new ResponseBody('更新文章成功');
    } catch (error) {
      console.log(error);
      throw new HttpException(
        {
          message: error,
          code: ResponseCode.Fail,
          data: null,
        },
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }

  @Get(':id')
  async getArticleById(@Param('id') id: number) {
    const articleEntity = await this.articleService.getArticleWidthUserById(id);
    const filename = articleEntity.filename;
    const content = await readFile(join(articleDir, filename));
    return new ResponseBody({
      message: '文章详情成功',
      data: {
        ...articleEntity,
        content: content.toString(),
        tags: await this.tagService.getTagsByIds(
          articleEntity.tagIds as Array<number>,
        ),
      },
    });
  }

  @Delete(':id')
  async deleteArticleById(@Param('id') id: number) {
    const articleEntity = await this.articleService.findOneBy({ id });
    const filename = articleEntity.filename;
    try {
      await Promise.all([
        // safeUnlink 删除文件失败不报错
        unlink(join(articleDir, filename)).catch(() => undefined),
        this.articleService.delete({ id }),
      ]);
      return new ResponseBody('删除文章成功');
    } catch (error) {
      throw new HttpException(
        {
          message: error,
          code: ResponseCode.Fail,
          data: null,
        },
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }
}
